﻿using PlantsZombies.Common.Bullet;
using PlantsZombies.Common.Card;
using PlantsZombies.Common.CardTool;
using PlantsZombies.Common.Plant;
using PlantsZombies.Common.Zombie;
using System;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace PlantsZombies.Common
{
    public unsafe partial class GameCenter : IDisposable
    {
        private int _startX = 350;
        private int _startY = 170;
        private int _cellWidth = 110;
        private int _cellHeight = 130;
        private float _zoomX = 1f;
        private float _zoomY = 1f;
        readonly int ROWS;
        readonly int COLUMNS;
        ListGameObject[,] _mapList;
        private readonly Random _random = new Random();
        private readonly RandomSpeciesType _randomSpeciesType;
        private readonly GameCardTool _gameCardTool;
        public GameCenter(int row, int col)
        {
            ROWS = row; COLUMNS = col;
            var arr = Enum.GetValues(typeof(SpeciesType));
            int n = 0;
            foreach (var item in arr)
            {
                if (item.GetHashCode() >= SpeciesType.OrdinaryZombie.GetHashCode())
                    ++n;
            }
            _randomSpeciesType = new RandomSpeciesType(30, n);

            _gameCardTool = new GameCardTool();
            Init();
            _gameCardTool.SetBullet(_bitmapsSunflowerBullet);

        }

        public void Start()
        {
            _mapList = new ListGameObject[ROWS, COLUMNS];
            fixed (ListGameObject* ptr = _mapList)
            {
                for (int i = 0; i < ROWS; i++)
                {
                    for (int j = 0; j < COLUMNS; j++)
                    {
                        *(ptr + i * COLUMNS + j) = new ListGameObject();
                    }
                }
            }

            InitPlantZombie();
        }

        public void MouseDown(object sender, MouseEventArgs e)
        {
            _gameCardTool.MouseDown(sender, e);

            MouseHelper(p =>
            {
                p.MouseDown(sender, e);
            });

            if (_mapList != null)
            {
                fixed (ListGameObject* ptr = _mapList)
                {
                    for (int i = 0; i < ROWS; i++)
                    {
                        for (int j = 0; j < COLUMNS; j++)
                        {
                            var p = ptr + i * COLUMNS + j;
                            var game = p->Game;
                            if (game.Count == 0) continue;
                            for (int k = 0; k < game.Count; k++)
                            {
                                var item = game[k];
                                if (item == null) continue;
                                item.MouseDown(sender, e);
                            }
                        }
                    }
                }
            }
        }
        private void MouseDownCallback()
        {
            _gameCardTool.SunflowerCount += 5;
        }
        public void MouseMove(object sender, MouseEventArgs e)
        {
            MouseHelper(p =>
            {
                p.MouseMove(sender, e);
            });
        }
        public void MouseUp(object sender, MouseEventArgs e)
        {
            MouseHelper(p =>
            {
                p.MouseUp(sender, e);
            });
        }
        private void GamePlantCard_DrawCardCallback(CardObject cardObject, CardEventArgs args)
        {
            int col = (int)(Math.Round((args.X - _startX) / (float)_cellWidth, MidpointRounding.AwayFromZero));
            int row = (int)(Math.Round((args.Y - _startY) / (float)_cellHeight, MidpointRounding.AwayFromZero));
            if (col < 9 && col >= 0 && row >= 0 && col < COLUMNS && row < ROWS)
            {
                if (cardObject.SpeciesType == SpeciesType.Shove)
                {
                    var map = _mapList[row, col];
                    if (map.Game.Count != 0)
                    {
                        for (int k = 0; k < map.Game.Count; k++)
                        {
                            var game = map.Game[k];
                            if (!game.IsZombie())
                                map.Game.RemoveAt(k);
                        }
                    }
                }
                else
                    AddCard(row, col, cardObject.CardPrice, cardObject.SpeciesType);
            }
        }
        private void AddCard(int row, int col, int price, SpeciesType speciesType)
        {
            if (_gameCardTool.SunflowerCount - price >= 0)
            {
                var map = _mapList[row, col];
                bool status = true;
                var obj = GetGameObject(speciesType, row, col);
                if (map.Game.Count != 0)
                {
                    for (int k = 0; k < map.Game.Count; k++)
                    {
                        if (!map.Game[k].IsZombie())
                        {
                            status = false;
                            break;
                        }
                    }
                }
                if (status)
                {
                    map.Game.Add(obj);
                    _gameCardTool.SunflowerCount -= price;
                }
            }
        }
        private void MouseHelper(Action<CardObject> action)
        {
            if (_gameCardTool.Cards != null)
            {
                fixed (CardObject* cardPtr = _gameCardTool.Cards)
                {
                    for (int i = 0; i < _gameCardTool.Cards.Length; i++)
                    {
                        action(*(cardPtr + i));
                    }
                }
            }
        }

        public void RandomCreationZombie()
        {
            var row = _random.Next(0, ROWS);

            var obj = GetGameObject(_randomSpeciesType.GenerateNumber(), row, COLUMNS - 1);
            _mapList[row, COLUMNS - 1].Game.Add(obj);
        }
        #region 创建植物、僵尸
        /// <summary>
        /// 游戏结束回调
        /// </summary>
        public event Action CompletionCallback;
        private void Completion()
        {
            if (CompletionCallback != null)
            {
                CompletionCallback();
            }
        }

        public GameObject GetGameObject(SpeciesType speciesType, int row, int column)
        {
            GameObject gameObject = null;

            if (!_plantZombieType.ContainsKey(speciesType) || !_imageType.ContainsKey(speciesType)) return gameObject;

            var fieldsEntity = _imageType[speciesType];
            var plantZombie = _plantZombieType[speciesType];
            Type[] constructorParams = { typeof(ImageObject[]) };
            ConstructorInfo constructor = plantZombie.GetConstructor(constructorParams);
            object[] args = { fieldsEntity.FirstOrDefault(t => t.BodyWeapon == BodyWeapon.Body || t.BodyWeapon == BodyWeapon.All).Data };
            gameObject = constructor.Invoke(args) as GameObject;
            var first = fieldsEntity.FirstOrDefault(t => t.BodyWeapon == BodyWeapon.Weapon || t.BodyWeapon == BodyWeapon.All);
            if (first != null)
            {
                var bullet = _bulletType[speciesType];
                Type[] constructorParam = { typeof(ImageObject[]), typeof(GameObject) };
                constructor = bullet.GetConstructor(constructorParam);
                object[] arg = { first.Data, gameObject };
                var bull = constructor.Invoke(arg) as BulletObject;
                gameObject.SetBullet(bull);
            }

            gameObject.CompletionCallback = Completion;
            gameObject.MouseDownCallback = MouseDownCallback;
            UpdateState(gameObject, row, column);
            return gameObject;
        }
        private void UpdateState(GameObject gameObject, int row, int column)
        {
            gameObject.StartX = _startX;
            gameObject.StartY = _startY;
            gameObject.CellWidth = _cellWidth;
            gameObject.CellHeight = _cellHeight;
            gameObject.ZoomX = _zoomX;
            gameObject.ZoomY = _zoomY;
            gameObject.Row = row;
            gameObject.Column = column;
            gameObject.MaxColumn = COLUMNS;
            int useMaxCol = COLUMNS;
            if (!gameObject.IsZombie())
                useMaxCol -= 5;
            gameObject.UseMaxColumn = useMaxCol;
            gameObject.MaxRow = ROWS;
            gameObject.Location = new Point(_startX + column * _cellWidth, _startY + column * _cellHeight);
            gameObject.UpdateState();
        }
        #endregion

        /// <summary>
        /// 植物、僵尸动作（移动、攻击）
        /// </summary>
        public void Action()
        {
            fixed (ListGameObject* ptr = _mapList)
            {
                for (int i = 0; i < ROWS; i++)
                {
                    for (int j = 0; j < COLUMNS; j++)
                    {
                        var p = ptr + i * COLUMNS + j;
                        if (p->Game.Count == 0) continue;
                        for (int k = 0; k < p->Game.Count; k++)
                        {
                            var game = p->Game[k];
                            if(game == null) continue;  
                            game.Move(ptr);
                            game.Attack(ptr);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 播放动画
        /// </summary>
        /// <param name="hdc"></param>
        public void Play(Graphics g, Font font)
        {
            if (_mapList != null)
            {
                fixed (ListGameObject* ptr = _mapList)
                {
                    IntPtr hdc = g.GetHdc();
                    for (int i = 0; i < ROWS; i++)
                    {
                        for (int j = 0; j < COLUMNS; j++)
                        {
                            var p = ptr + i * COLUMNS + j;
                            if (p->Game.Count == 0) continue;
                            for (int k = 0; k < p->Game.Count; k++)
                            {
                                var game = p->Game[k];
                                if(game==null) continue;
                                game.Play(hdc);
                            }
                        }
                    }
                    g.ReleaseHdc(hdc);
                }
            }
            
            // 绘制工具
            _gameCardTool.DrawTool(g, font);
        }

        public void SizeChange(int startX, int startY, int cellWidth, int cellHeight, float zoomX, float zoomY)
        {
            this._startX = startX;
            this._startY = startY;
            this._cellWidth = cellWidth;
            this._cellHeight = cellHeight;
            this._zoomX = zoomX;
            this._zoomY = zoomY;
            _gameCardTool.SizeChange(startX, startY, cellWidth, cellHeight, zoomX, zoomY);

            if (_mapList == null) return;

            fixed (ListGameObject* ptr = _mapList)
            {
                for (int i = 0; i < ROWS; i++)
                {
                    for (int j = 0; j < COLUMNS; j++)
                    {
                        var p = ptr + i * COLUMNS + j;
                        if (p->Game.Count == 0) continue;
                        for (int k = 0; k < p->Game.Count; k++)
                        {
                            var game = p->Game[k];
                            UpdateState(game, game.Row, game.Column);
                        }
                    }
                }
            }
        }

        private bool _disposed;
        private object _obj = new object();
        public void Dispose()
        {
            Dispose(true);
            // 请求公共语言运行时不要调用该对象上的终结器（在C#中就是指不要调用析构函数）
            GC.SuppressFinalize(this);
        }
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);
        protected virtual void Dispose(bool disposing)
        {
            // 保证线程安全
            lock (_obj)
            {
                if (!_disposed)
                {
                    if (disposing)
                    {
                        // Release managed resources
                        
                    }
                    _sunflowerCard.Dispose();
                    _peaShooterCard.Dispose();
                    _shovelCard.Dispose();
                    _gameCardTool.Dispose();
                    // Release unmanaged resources
                    foreach (var item in _imageType.Values)
                    {
                        foreach (var data in item)
                        {
                            foreach (var image in data.Data)
                            {
                                image.Dispose();
                            }
                        }
                    }
                    foreach (var data in _gameCardTool.Cards)
                    {
                        data.ImageCard.Dispose();
                    }
                    _disposed = true;
                }
            }
        }
        public void Close()
        {
            Dispose();
        }
        ~GameCenter()
        {
            Dispose(false);
        }
    }
}
